package com.mapeditor.math {
    public class Vector3 {
        public var x:Number;
        public var y:Number;
        public var z:Number;
        public var vec:Vector3;
        
        public function Vector3(px:Number=0, py:Number=0, pz:Number=0) {
            x = px;
            y = py;
            z = pz;
        }

        public function lenghtSqr():Number
       {
          return Math.sqrt(x*x + y*y + z*z);
       }
        
        public function toString():void {
            trace("["+x+","+y+","+z+"]");
        }
        
    
        public function reset(px:Number, py:Number, pz:Number):void {
            x = px;
            y = py;
            z = pz;
        }
        public function resetNew(px:Number, py:Number, pz:Number):Vector3 {
            return (new Vector3((x=px), (y=py), (z=pz)));
        }
        
        public function cloneVec(vec:Vector3):void {
            x = vec.x;
            y = vec.y;
            z = vec.z;
        }
        
        public function cloneVecNew(vec:Vector3):Vector3 {
            return (new Vector3((x=vec.x), (y=vec.y), (z=vec.z)));
        }
        
        public function incrX(xScalar:Number):void {
            x += xScalar;
        }
        
        public function incrY(yScalar:Number):void {
            y += yScalar;
        }
        
        public function incrZ(zScalar:Number):void {
            z += zScalar;
        }
        
        public function incrXY(xScalar:Number, yScalar:Number):void {
            x += xScalar;
            y += yScalar;
        }
        
        public function incrXZ(xScalar:Number, zScalar:Number):void {
            x += xScalar;
            z += zScalar;
        }
        
        public function incrYZ(yScalar:Number, zScalar:Number):void {
            y += yScalar;
            z += zScalar;
        }
        
        public function incrXYZ(xScalar:Number, yScalar:Number, zScalar:Number):void {
            x += xScalar;
            y += yScalar;
            z += zScalar;
        }
        
        public function addVec(vec:Vector3):void {
            x += vec.x;
            y += vec.y;
            z += vec.z;
        }
        
        public function addVecNew(vec:Vector3):Vector3 {
            return (new Vector3((x + vec.x), (y + vec.y), (z + vec.z)));
        }
        
        public function subVec(vec:Vector3):void {
            x -= vec.x;
            y -= vec.y;
            z -= vec.z;
        }
        public function multVec(vec:Vector3):void {
            x *= vec.x;
            y *= vec.y;
            z *= vec.z;
        }
        public function divVec(vec:Vector3):void {
            x /= vec.x;
            y /= vec.y;
            z /= vec.z;
        }
        public function subVecNew(vec:Vector3):Vector3 {
            return (new Vector3((x - vec.x), (y - vec.y), (z - vec.z)));
        }
        public function getNegativeVec(vec:Vector3):void {
            x = -x;
            y = -y;
            z = -z;
        }
        
        public function getNegativeVecNew(vec:Vector3):Vector3 {
            return (new Vector3((x=-x), (y=-y), (z=-z)));
        }
        
        public function scaleVec(scalar:Number):void {
            x *= scalar;
            y *= scalar;
            z *= scalar;
        }
        
        public function scaleVecNew(scalar:Number):Vector3 {
            return (new Vector3((x *= scalar), (y *= scalar), (z *= scalar)));
        }
        
        
        public function dot(vec:Vector3):Number {
            return ((x*vec.x)+(y*vec.y));
        }
        public function angle(vec:Vector3):Number
       {
          var dp:Number;
          var angPI:Number;
        
          dp = this.dot(vec); //dot product
          if(dp >= 1) dp = 1;
          if(dp <=-1) dp =-1;
        
          angPI = Math.acos(dp);
    
          //determina a posicao relativa do vetor
          return this.side(vec) * angPI;
          //return angPI;
       }
       public function side(v:Vector3):Number
       {
          if (this.y*v.x > this.x*v.y)
             return -1;
          else
            return 1;
       }
        

        
        public function cross(vec:Vector3):Vector3 {
            var cross:Vector3 = new Vector3(0, 0, 0);
            cross.x = (y*vec.z)-(z*vec.y);
            cross.y = (z*vec.x)-(x*vec.z);
            cross.z = (x*vec.y)-(y*vec.x);
            return cross;
        }
        public function cross2D(vec:Vector3):Vector3 {
            var cross:Vector3 = new Vector3(0, 0, 0);
            cross.x = (y*vec.x)-(x*vec.y);
            cross.y = (x*vec.y)-(y*vec.x);
            return cross;
        }
        
        
        public function getNormal():Number {
            return (Math.sqrt((x*x)+(y*y)));
        }
        
        public function normalize():void {
            var norm:Number = getNormal();
            x /= norm;
            y /= norm;      
        }
        public function getUnitVec():Vector3 {
            var unit:Vector3 = new Vector3(x, y, z);
            var norm:Number = getNormal();
            unit.x /= norm;
            unit.y /= norm;
            unit.z /= norm;
            return (unit);
        }
        public function distance(v:Vector3):Number{
            var dx:Number = x-v.x;
            var dy:Number = y-v.y;
            var dz:Number = z-v.z;
            return Math.sqrt(dx*dx + dy*dy + dz*dz);
        }
		
		public function distanceToReference(v:Vector3):Number{
            var dx:Number = x-v.x;
            var dy:Number = y-v.y;
            var dz:Number = z-v.z;
            return (dx*dx + dy*dy + dz*dz);
        }
        
        public function compareVec(v:Vector3):Boolean{
        
            if(x == v.x && y == v.y )
                return true;
            else    
                return false;
        }
        
        public function LineIntersection2D(A:Vector3, B:Vector3, C:Vector3, D:Vector3):Boolean
        {
           
           var rTop:Number = (A.y-C.y)*(D.x-C.x)-(A.x-C.x)*(D.y-C.y);
           var rBot:Number = (B.x-A.x)*(D.y-C.y)-(B.y-A.y)*(D.x-C.x);
        
           var sTop:Number = (A.y-C.y)*(B.x-A.x)-(A.x-C.x)*(B.y-A.y);
           var sBot:Number = (B.x-A.x)*(D.y-C.y)-(B.y-A.y)*(D.x-C.x);
            
            
           if ( (rBot == 0) || (sBot == 0))
           {
              //lines are parallel
              return false;
           }
           var r:Number = rTop/rBot;
           var s:Number = sTop/sBot;
        
           if( (r >= 0) && (r <= 1) && (s >= 0) && (s <= 1) )
           {
              //point = A + (B - A) * r;
              var BA:Vector3 = B.subVecNew(A);
              var BAr:Vector3 = BA.scaleVecNew(r);
              A.addVec(BAr);
              
              this.x = A.x;
              this.y = A.y;
              return true;
           }
           return false;
        }
        
        /**
       Verifica as arestas e calcula o ponto de intersecao do fluxo 
       com uma das arestas
       pi -> ponto inicial do fluxo
       px,py -> Posicao real do item
       w,h   -> largura, altura do item (serve para encontrar o centro)
       */
       public function getPointIntersectImage(pi:Vector3,pos:Vector3,w:Number,h:Number,points:Array):Vector3
       {
           
           
            var A:Vector3 = new Vector3();          
           var B:Vector3 = new Vector3();
           
           var C:Vector3 = new Vector3(pi.x,pi.y);
           var D:Vector3 = new Vector3(pos.x + w/2,
                                       pos.y + h/2);
           
           var i:Number = 0;
           var size:Number = points.length;
           var point:Vector3 = new Vector3();
           var fgIntersect:Boolean = false;
            
           for(i = 0; i < size; i++){
               A.x = pos.x + points[i].x;
               A.y = pos.y + points[i].y;
               
               if(i+1>=size)
               {
                   B.x = pos.x+ points[0].x; 
                   B.y = pos.y+ points[0].y;
               }
               else
               {
                   B.x = pos.x+ points[i+1].x; 
                   B.y = pos.y+ points[i+1].y;
               }
               
               fgIntersect= point.LineIntersection2D(A,B,C,D);
               if(fgIntersect)
                   break;
               
           }
           //utilizando variavel pos para retorno
           if(fgIntersect)
           {
               return point;
           }
           return pi;  
       }
        
    
	
		public function distancePointLine(p1:Vector3, p2:Vector3):Vector3 {
			
			var delta:Vector3 = p2.subVecNew(p1);
			
			if ((delta.x == 0) && (delta.y == 0)) {
				trace("p1 and p2 cannot be the same point");
				return p1;
			}

			var u:Number = ((x - p1.x) * delta.x + (y - p1.y) * delta.y) / (delta.x * delta.x + delta.y * delta.y);
			//trace("u =" + u);
			var closestPoint:Vector3;
			if (u < 0) {
				closestPoint = p1;
			} else if (u > 1) {
				closestPoint = p2;
			} else {
				closestPoint = new Vector3(p1.x + u * delta.x, p1.y + u * delta.y);
			}

			return closestPoint;
		}
	
	}

    //ends class
}